import java.util.Random;


//------------------------------------------------------------//
//Time		|    Action							 | Personne	  //
//------------------------------------------------------------//
//			|Creation of the class				 |			  //
//04/07/2013|Implement validation for constraint | JP. Do	  //
//			|Implement changeStringToIntMonth()  |			  //
//------------------------------------------------------------//
//			|Implement printError()			  	 |			  //
//16/07/2013|Implement isStringNumber()			 | JP. Do	  //
//			|Implement roundingDouble()			 |			  //
//------------------------------------------------------------//
//25/07/2013|Implement getRandomData()			 | E. Faucher //
//------------------------------------------------------------//



/***
 * Utility
 * -------------------------------
 * This class is used to do simple validation
 * like if the data is between the constraint or
 * if it is a String number. We used static method
 * in this class to easier used those method for 
 * extern class.
 * 
 * @author Jean-Paul Do
 *
 */
public class Utility {
	private static double value; //a temp value that use for processing in method
	private static String errorStr;

	/**
	 * changeStringToIntMonth()
	 * -----------------------
	 * This method is used to convert string month to int month.
	 * It supports french and english abbreviation of the month.
	 * 
	 * ex.: jan --> 1
	 * 
	 * @param month
	 * 		String having the abbreviation of the month
	 * @return int
	 * 		the month in int | error 
	 */
	public static int changeStringToIntMonth(String month, boolean printError){
		value = Constant.ERROR_MONTH;

		switch(month.toUpperCase().charAt(0)){
		case 'A':
			if (month.equalsIgnoreCase(Constant.APR_EN) || month.equalsIgnoreCase(Constant.APR_FR))
				value = Constant.APR;
			else if (month.equalsIgnoreCase(Constant.AUG_EN) || month.equalsIgnoreCase(Constant.AUG_FR))
				value = Constant.AUG;
			break;
			
		case 'D':
			if (month.equalsIgnoreCase(Constant.DEC_EN) || month.equalsIgnoreCase(Constant.DEC_FR))
				value = Constant.DEC;
			break;
			
		case 'F':
			if (month.equalsIgnoreCase(Constant.FEB_EN) || month.equalsIgnoreCase(Constant.FEB_FR))
				value = Constant.FEB;
			break;
			
		case 'J':
			if (month.equalsIgnoreCase(Constant.JAN_EN) || month.equalsIgnoreCase(Constant.JAN_FR))
				value = Constant.JAN;
			else if(month.equalsIgnoreCase(Constant.JUN_EN) || month.equalsIgnoreCase(Constant.JUN_FR))
				value = Constant.JUN;
			else if(month.equalsIgnoreCase(Constant.JUL_EN) || month.equalsIgnoreCase(Constant.JUL1_FR)  || month.equalsIgnoreCase(Constant.JUL2_FR))
				value = Constant.JUL;
			break;

		case 'M':
			if (month.equalsIgnoreCase(Constant.MAR_EN) || month.equalsIgnoreCase(Constant.MAR_FR))
				value = Constant.MAR;
			else if (month.equalsIgnoreCase(Constant.MAY_EN) || month.equalsIgnoreCase(Constant.MAY_FR))
				value = Constant.MAY;
			break;
			
		case 'N':
			if (month.equalsIgnoreCase(Constant.NOV_EN) || month.equalsIgnoreCase(Constant.NOV_FR))
				value = Constant.NOV;
			break;

		case 'O':
			if (month.equalsIgnoreCase(Constant.OCT_EN) || month.equalsIgnoreCase(Constant.OCT_FR))
				value = Constant.OCT;
			break;
			
		case 'S':
			if (month.equalsIgnoreCase(Constant.SEP_EN) || month.equalsIgnoreCase(Constant.SEP_FR))
				value = Constant.SEP;
			break;

		default:
			break;	
		} //end swtich

		if(printError)
			//print error
			value = errorPrint(value);
		
		return (int) value;
	} //changeStringToIntMonth()
	
	
	/***
	 * validHumidity()
	 * -----------------------------------
	 * This method is used to valid humidity number
	 * 
	 * @param humidity
	 * 		humidity variable
	 * @return double
	 * 		humity variable | error_humidity
	 */
	public static double validHumidity(double humidity, boolean printError){
		value = humidity;
		if (value < Constant.MIN_HUMIDITY || value > Constant.MAX_HUMIDITY)
			value = Constant.ERROR_HUMIDITY;
		
		if(printError)
			//print error
			value = errorPrint(value);
		return value;
	}//validHumidity()
	
	/**
	 * validWind()
	 * -----------------------------------
	 * This method is used to valid windSpeed number
	 * 
	 * @param windSpeed
	 * 		windSpeed variable
	 * @return double
	 * 		windSpeed variable | error_wind
	 */
	public static double validWind(double windSpeed, boolean printError){
		value = windSpeed;
		if (value < Constant.MIN_WIND || value > Constant.MAX_WIND)
			value = Constant.ERROR_WIND;
	
		if(printError)
			//print error
			value = errorPrint(value);
		return value;
	}//validWind()
	
	/**
	 * validRain()
	 * -----------------------------------
	 * This method is used to valid rain number
	 * 
	 * @param rain
	 * 		rain variable
	 * @return double
	 * 		rain variable | error_rain
	 */
	public static double validRain(double rain, boolean printError){
		value = rain;
		if (value < Constant.MIN_RAIN || value > Constant.MAX_RAIN)
			value = Constant.ERROR_RAIN;
		if(printError)
			//print error
			value = errorPrint(value);
		return value;
	}//validRain()
	
	/**
	 * validBurnSurface()
	 * -----------------------------------
	 * This method is used to valid burn surface number
	 * 
	 * @param burnSurface
	 * 		burnSurface variable
	 * @return double
	 * 		burnSurface variable | error_burn_surface
	 */
	public static double validBurnSurface(double burnSurface, boolean printError){
		value = burnSurface;
		if (value < Constant.MIN_BURN_SURFACE || value > Constant.MAX_BURN_SURFACE)
			value = Constant.ERROR_BURN_SURFACE;
		
		if(printError)
			//print error
			value = errorPrint(value);
		return value;
	}//validBurnSurface()
	
	/**
	 * validCelcius()
	 * -----------------------------------
	 * This method is used to valid celcius number
	 * 
	 * @param celcius
	 * 		celcius variable
	 * @return double
	 * 		celcius variable | error_celcius
	 */
	public static double validCelcius(double celcius, boolean printError){
		value = celcius;
		if (value < Constant.MIN_CELCIUS || value > Constant.MAX_CELCIUS)
			value = Constant.ERROR_CELCIUS;
		if(printError)
			//print error
			value = errorPrint(value);
		return value;
	}//validCelcius()
	
	/**
	 * isStringNull()
	 * -----------------------------------------
	 * This method is used to validate if the string is empty or null
	 * @param string
	 * 		string value
	 * @return boolean
	 * 		true | false
	 */
	public static boolean isStringNull(String string){
		if(string.equals(null) || string.isEmpty())
			return true;
		return false;
	}//isStringNull()

	/**
	 * isStringNumber()
	 * --------------------------------------------
	 * This method is used to validate if the string is a number
	 * 
	 * @param number
	 * 		containt the string
	 * @return boolean
	 * 		true -> is a number | false -> is not a number
	 */
	public static boolean isStringNumber(String number){
		//check if it is null
		if (isStringNull(number))
			return false;
		
		//check for each character if it is a number 
		for(int idx = 0; idx < number.length(); idx++)
			if(!Character.isDigit(idx))
				return false;

		//it is a  number
		return true;
	}
	

	public static double errorPrint(double typeError){
		if (!(typeError < 0))
			return typeError;
		
		return errorPrint((int)typeError);
	}
	
	/**
	 * errorPrint()
	 * ----------------------------------
	 * This method will print the error that was set before.
	 * 
	 * @param typeError
	 * 		the type of error
	 * 
	 * 
	 */
	public static double errorPrint(int typeError){
		//end this method if it isn't an error type
		if (!(typeError < 0))
			return typeError;
		
		double minInfo = -1;
		
		//find the error type
		switch (typeError){
		case Constant.ERROR:
			errorStr = "Unknown error in runtime";
			break;

		case Constant.ERROR_MONTH:
			errorStr = "Month abbraviation is unknown";
			minInfo = Constant.JAN;
			break;

		case Constant.ERROR_HUMIDITY:
			errorStr = "Relative humidity variable is out of bound. It should be between " + Constant.MIN_HUMIDITY + " to " + Constant.MAX_HUMIDITY;
			minInfo = Constant.MIN_HUMIDITY;
			break;

		case Constant.ERROR_WIND:
			errorStr = "Wind Speed variable is out of bound. It should be between " + Constant.MIN_WIND + " to " + Constant.MAX_WIND;
			minInfo = Constant.MIN_WIND;
			break;

		case Constant.ERROR_RAIN:
			errorStr = "Rain variable is out of bound. It should be between " + Constant.MIN_RAIN + " to " + Constant.MAX_RAIN;
			minInfo = Constant.MIN_RAIN;
			break;

		case Constant.ERROR_BURN_SURFACE:
			errorStr = "Burning surface variable is out of bound. It should be between " + Constant.MIN_BURN_SURFACE + " to " + Constant.MAX_BURN_SURFACE;
			minInfo = Constant.MIN_BURN_SURFACE;
			break;

		case Constant.ERROR_UNKNOWN_VAR:
			errorStr = "variable is not a number";
			break;
		
		case Constant.ERROR_CELCIUS:
			errorStr = "Celcius variable is out of bound. It should be between " + Constant.MIN_CELCIUS + " to " + Constant.MAX_CELCIUS;
			minInfo = Constant.MIN_CELCIUS;
			break;
			
		case Constant.ERROR_FILE_WRONG_FORMAT:
			errorStr = "The readed file has the wrong format for the data ! Please, check your file first before letting the system read it.";
			break;

		default:
			errorStr = "Not supported error!";
			break;
		}
		
		//print error type
		System.out.println("WARNING: " + errorStr);
		
		return minInfo;
	} //errorPrint()
	
	
	/**
	 * roundingDouble()
	 * --------------------------------------
	 * This method allows us to retrieve the number 
	 * after the comma/dot. It's also round your the number
	 * 
	 * ex: If us data is 9.25752
	 * A)the nbNumberAfterComma is 2
	 * roundingDouble(9.25752, 2) => 9.26
	 * 
	 * B)the nbNumberAfterComma is 1
	 * roundingDouble(9.25752, 1) => 9.2
	 * 
	 * C)the nbNumberAfterComma is 3
	 * roundingDouble(9.25752, 3) => 9.258
	 * 
	 * @param data
	 * 		the double that we want to shortcut
	 * @param nbNumberAfterComma
	 * 		the max number after the comma/dot
	 * @return double
	 * 		the new double
	 */
	public static double roundingDouble(double data, int nbNumberAfterComma){
		if (nbNumberAfterComma < 0)
			return data;
		
		//get the value after the number after the comma
		value = data % (1/Math.pow(10,nbNumberAfterComma));
		//removing the excess number after the comma
		data = data - value;
		//get the number first of the delete part
		value = (int) Math.pow(10,nbNumberAfterComma+1)*value;
		//if the number is higher than 5, round the number
		if(value > 5){
			data += 1/Math.pow(10,nbNumberAfterComma);
		}
		
		//if the number is equal to 5, verify if the last digit is odd
		else if (value == 5){
			value =(data % 1/Math.pow(10,nbNumberAfterComma - 1))*Math.pow(10,nbNumberAfterComma);
			//if last digit is odd, round it
			if (value % 2 == 1){
				data += 1/Math.pow(10,nbNumberAfterComma);
			}
		}	
		return data;
	}

	/**
	 * 
	 * @return a random stream of Data
	 */
	public static Data getRandomData()
	{
		Random myRandom = new Random();
		
		int myMonth = myRandom.nextInt(11)+1;

		String myMonthString = "";
		switch(myMonth)
		{
			case 1 : 
				myMonthString = Constant.JAN_EN;
				break;
			case 2 : 
				myMonthString = Constant.FEB_EN;
				break;
			case 3 : 
				myMonthString = Constant.MAR_EN;
				break;
			case 4 : 
				myMonthString = Constant.APR_EN;
				break;
			case 5 : 
				myMonthString = Constant.MAY_EN;
				break;
			case 6 : 
				myMonthString = Constant.JUN_EN;
				break;
			case 7 : 
				myMonthString = Constant.JUL_EN;
				break;
			case 8 : 
				myMonthString = Constant.AUG_EN;
				break;
			case 9 : 
				myMonthString = Constant.SEP_EN;
				break;
			case 10 : 
				myMonthString = Constant.OCT_EN;
				break;
			case 11 : 
				myMonthString = Constant.NOV_EN;
				break;
			case 12 : 
				myMonthString = Constant.DEC_EN;
				break;
			default :
				myMonthString = Constant.JAN_EN;
				break;
		}
		
		double myCelcius = myRandom.nextDouble()*Constant.MAX_CELCIUS;
		while(Utility.validCelcius(myCelcius,false) != myCelcius)
		{
			myCelcius = myRandom.nextDouble()*Constant.MAX_CELCIUS;
		}
		
		double myHumidity = myRandom.nextDouble()*Constant.MAX_HUMIDITY;
		while(Utility.validHumidity(myHumidity,false) != myHumidity)
		{
			myHumidity = myRandom.nextDouble()*Constant.MAX_HUMIDITY;
		}
		
		double myWindSpeed = myRandom.nextDouble()*Constant.MAX_WIND;
		while(Utility.validWind(myWindSpeed, false) != myWindSpeed)
		{
			myWindSpeed = myRandom.nextDouble()*Constant.MAX_WIND;
		}
		
		double myRain = myRandom.nextDouble()*Constant.MAX_RAIN;
		while(Utility.validRain(myRain, false) != myRain)
		{
			myRain = myRandom.nextDouble()*Constant.MAX_RAIN;
		}
		
		Data myData = new Data(myMonthString, roundingDouble(myCelcius,2), roundingDouble(myHumidity,1), roundingDouble(myWindSpeed,2), roundingDouble(myRain,1));
		myData.setTrainingData(false);
		return myData;
	}
}
